Skip to content

Fix parsing of table check constraints in CREATE TABLE - #684

Open
xmaks82 wants to merge 1 commit into
phpmyadmin:5.11.xfrom
xmaks82:create-table-check-constraint
Open

xmaks82 wants to merge 1 commit into
phpmyadmin:5.11.xfrom
xmaks82:create-table-check-constraint

Conversation

@xmaks82

@xmaks82 xmaks82 commented Oct 23, 2026

Copy link
Copy Markdown

Ref #167

A table level check constraint broke parsing of the whole CREATE TABLE, with any context (MySQL 8.0.16+ and MariaDB 10.2.1+ both print it in SHOW CREATE TABLE):

CREATE TABLE t (a INT, b INT, CONSTRAINT `chk_ab` CHECK (a < b), c INT)

Before:

A symbol name was expected! A reserved keyword can not be used as a column name without backquotes. (near "CHECK")
Unexpected beginning of statement. (near "a")
Unexpected beginning of statement. (near "b")
Unexpected beginning of statement. (near "c")
Unrecognized statement type. (near "INT")

and build() gave CREATE TABLE t (`a` int, `b` int): the constraint and column c were lost. The same happens for an unnamed CHECK (a > 0) and for CONSTRAINT CHECK (...).

Now CreateDefinition accepts [CONSTRAINT [symbol]] CHECK (expr) and stores it like a column check: name is the constraint symbol (or null), isConstraint is set for CONSTRAINT, and the expression is in options (the existing CHECK field option, so [NOT] ENFORCED works too). Utils\Table::getFields() skips it because it has no type.

Column level CHECK and ALTER TABLE ... ADD CONSTRAINT ... CHECK are not changed.

Docs:

Tests: parser fixture parseCreateTableCheckConstraint (named, unnamed, CONSTRAINT CHECK, NOT ENFORCED, the example from #167, a column after the constraints) and a round-trip test in tests/Builder/CreateStatementTest.php.

A table level [CONSTRAINT [symbol]] CHECK (expr) definition was
reported as a column named CHECK, and the rest of the statement
was lost. Parse it as a create definition with the CHECK option.

Ref phpmyadmin#167

Signed-off-by: Магда М.Г. <mail@mgmagda.ru>

@by-ae by-ae left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked this out and ran phpunit on the branch: 1065 tests, all pass.

I also fed it a few real SHOW CREATE TABLE shapes that aren't in the new fixture, and they all parse with no errors and build back unchanged:

  • MySQL 8 output with doubled parens and the version comment: CONSTRAINT `chk` CHECK ((`a` < `b`)) /*!80016 NOT ENFORCED */ builds as CONSTRAINT `chk` CHECK ((`a` < `b`)) NOT ENFORCED. On 5.11.x the same statement gives 4 errors and drops the constraint.
  • MariaDB output that mixes a column-level CHECK (json_valid(`j`)) with a table-level CONSTRAINT `CONSTRAINT_1` CHECK (`a` > 0).
  • Nested parens in an unnamed check, CHECK (a IN (1,2) AND (b > 0)), followed by another column.
  • CONSTRAINT c1 CHECK (a > 0) ENFORCED, and CONSTRAINT CHECK (...) followed by PRIMARY KEY (a).

Since the check constraint now shows up in $statement->fields with no type or key, I made sure the helpers in Utils\Table still behave. On a table that has a FOREIGN KEY and a CHECK constraint, Table::getFields() returns just the real columns (j,a,b) and Table::getForeignKeys() returns only fk, the same as on 5.11.x. Both skip entries with an empty type/key, so the new entry doesn't leak into either one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants